home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 January: Mac OS SDK / Dev.CD Jan 97 SDK2.toast / Development Kits (Disc 2) / OpenDoc / OpenDoc Development / Debugging Support / OpenDoc™ Source Code / Storage / Bento / CM / TOCObjs.c < prev    next >
Encoding:
Text File  |  1996-08-28  |  54.0 KB  |  1,151 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:         TOCObjs.c  
  3.  
  4.     Contains:    Container Manager TOC Objects Routines
  5.  
  6.     Written by:    Ira L. Ruben
  7.  
  8.     Owned by:    Ed Lai
  9.  
  10.     Copyright:    © 1991-1994 by Apple Computer, Inc., all rights reserved.
  11.  
  12.     Change History (most recent first):
  13.  
  14.          <2>     8/26/94    EL        #1182275 Matches TOCObjes.h.
  15.          <2>     3/31/94    EL        Add cmPutObjectInTOC call. #1150214
  16.          <1>      2/3/94    EL        first checked in
  17.          <4>      2/2/94    EL        cmFreeLastDeletedObject no longer needed,
  18.                                                     use refNumHandling to control freeing.
  19.          <3>    10/29/93    EL        Add new call cmFreeLastDeletedObject so we
  20.                                                     can free the reference associated with
  21.                                                     value
  22.          <2>     10/4/93    EL        Add cmGetMasterListTail call.
  23.  
  24.     To Do:
  25. */
  26.  
  27. /*---------------------------------------------------------------------------*
  28.  |                                                                           |
  29.  |                           <<<  TOCObjs.c   >>>                            |
  30.  |                                                                           |
  31.  |                  Container Manager TOC Objects Routines                   |
  32.  |                                                                           |
  33.  |                               Ira L. Ruben                                |
  34.  |                                 11/20/91                                  |
  35.  |                                                                           |
  36.  |                  Copyright Apple Computer, Inc. 1991-1994                 |
  37.  |                           All rights reserved.                            |
  38.  |                                                                           |
  39.  *---------------------------------------------------------------------------*
  40.  
  41.  All TOC object manipulation routines are contained in this file. Only manipulations down
  42.  to the object level are done here.  The objects themselves contain pointers off to lower
  43.  level structures which are maintained by TOCEntries.c. The split is done this way to keep
  44.  the accessing of objects separate from the stuff represented by those objects.
  45.  
  46.  TOC objects are based on their ID's.  The ID's do not lend themselves well to the 
  47.  standard binary-tree mechanism we use elsewhere so another scheme is used here.
  48.  
  49.  TOC objects are access through a set of index tables.  The tables correspond to "powers"
  50.  of a chosen index table size.  For example, if the size was 100 and we had ID 1234567 we
  51.  would have 4 indices: 01, 23, 45, 67.  Four index tables would exist each corresponding
  52.  to the indices 00 to 99.  The first table would have its 01'th entry pointing to the next
  53.  table.  That next table would have its 23rd entry pointing to the third table.  The 
  54.  third table would have its 45th entry pointing to the last table.  The 67th entry in the
  55.  last table would point to the actual object with ID 1234567.
  56.  
  57.  The actual number of tables is dependent on the table size and the max ID value supported
  58.  (which is 0xFFFFFFFF).  Small ids are padded with 00 as the higher order indices.
  59.  
  60.  The number of tables is dependent on the table size and the sparseness of the ID's
  61.  presented.  This scheme represents a comprimise between speed, space, and the fact that
  62.  we want to always have the ID's in sorted order.  We get the sorted order by walking the
  63.  index tables in the appropriate way.
  64.  
  65.  The support for this scheme has been generalized to allow any size index table. 
  66.  
  67.  Multiple TOC's are supported.  Each TOC is tied to a anonymous reference number from
  68.  the outside caller's point of view.  In here it is a pointer to a control block that
  69.  contains the root pointer to the TOC index tables and the control variables to access
  70.  through the index tables mentioned above.
  71.  
  72.  Each TOC is independent.  Since the control variables are tied to the TOC, it is allowed
  73.  to have different size index tables for different TOC.  It's not clear this has any
  74.  benefit, but it's allowed.
  75.  
  76.  The TOC routines only know the final TOC entries down to the level of "objects".  The
  77.  objects themselves contain fields maintained by other routines outside the scope of this
  78.  file.  In particular the object is mainly a "head cell" for a list of entries chained
  79.  off of it. The  ListMgr package is used to maintain the ListMgr by the caller by using the
  80.  object.  Other fields are provided as well which are also maintained by the caller.  The
  81.  only exception is the object ID number which is known to this file.
  82. */
  83.  
  84.  
  85. #include <stddef.h>
  86. #include <string.h>
  87. #include <setjmp.h>
  88. #include <limits.h>
  89. #include <stdio.h>
  90.  
  91. #ifndef __CMTYPES__
  92. #include "CMTypes.h"
  93. #endif
  94. #ifndef __CM_API_TYPES__
  95. #include "CMAPITyp.h"
  96. #endif
  97. #ifndef __LISTMGR__
  98. #include "ListMgr.h"
  99. #endif
  100. #ifndef __TOCENTRIES__
  101. #include "TOCEnts.h"   
  102. #endif
  103. #ifndef __TOCOBJECTS__
  104. #include "TOCObjs.h"   
  105. #endif
  106. #ifndef __CONTAINEROPS__
  107. #include "Containr.h"  
  108. #endif
  109. #ifndef __UPDATING__
  110. #include "Update.h"  
  111. #endif
  112. #ifndef __HANDLERS__
  113. #include "Handlers.h"
  114. #endif
  115. #ifndef __SESSIONDATA__
  116. #include "Session.h"          
  117. #endif
  118.  
  119.                                                                     CM_CFUNCTIONS
  120.  
  121. /* The following generates a segment directive for Mac only due to 32K Jump Table             */
  122. /* Limitations.  If you don't know what I'm talking about don't worry about it.  The        */
  123. /* default is not to generate the pragma.  Theoritically unknown pragmas in ANSI won't    */
  124. /* choke compilers that don't recognize them.  Besides why would they be looked at if        */
  125. /* it's being conditionally skipped over anyway?  I know, famous last words!                        */
  126.  
  127. #if CM_MPW
  128. #pragma segment TOCObjects
  129. #endif
  130.  
  131.  
  132. #define MaxValue ULONG_MAX                            /* 0xFFFFFFFF on MC68XXX's                                            */
  133.  
  134. typedef void **Indices;                                    /* index tables are arrays of pointers                    */
  135.  
  136. /* All objects in a TOC are linked together on a single master object list chain.  Also    */
  137. /* all properties and types are chained on separate lists which are subsets of the full */
  138. /* object chain.  There are fields in an object for all three of these chain links.         */
  139. /* Since types and properties are mutually exclusive objects, then, of course we need     */
  140. /* only one link for either of these chains.  But there are three chain headers, and         */
  141. /* these headers, defined below, are kept in the TOC control block and manipulated by        */
  142. /* cmLinkObject() and cmUnLinkObject().                                                                                                    */
  143.  
  144. struct ObjectListHdrs {                                    /* Layout of master lists object chain headers:    */
  145.     TOCObjectPtr objectsListHead;                    /*         list of all objects, props, and types            */
  146.     TOCObjectPtr objectsListTail;                    /*        last object, property, or type created        */
  147.     
  148.     TOCObjectPtr propertiesListHead;            /*        list of all property objects                            */
  149.     TOCObjectPtr propertiesListTail;            /*        last property object created                            */
  150.     
  151.     TOCObjectPtr typesListHead;                        /*        list of all type objects                                    */
  152.     TOCObjectPtr typesListTail;                        /*        last type object created                                    */
  153. };
  154. typedef struct ObjectListHdrs ObjectListHdrs;
  155.  
  156.  
  157. /* The following is the "control block" that is used to access an in-memory TOC.  This    */
  158. /* is privately known only to this file.  Outside this is referred to via an anonymous    */
  159. /* "void *" pointer.                                                                                                                                        */
  160.  
  161. struct TOC {                                                        /* Layout for a TOC "control block":                        */
  162.     Indices            toc;                                        /*         the root of the toc                                                */
  163.     CM_SHORT               nbrOfIndices;                    /*         nbr of index table levels (table depth)        */
  164.     CM_ULONG           tableSize;                            /*         size of an index table                                        */
  165.     CMBoolean              powerOfTwo;                        /*         true ==> can access index by shift/mask        */
  166.     CM_SHORT               indexShift;                        /*         shift value to access an index(powerOfTwo)*/
  167.     CM_ULONG           indexMask;                            /*         mask value to access an index (powerOfTwo)*/
  168.     ContainerPtr     container;                            /*        ptr to "owning" container control block        */
  169.     ObjectListHdrs masterLists;                        /*         "master lists" of all objs, props, types    */
  170.     TOCObjectPtr     deletedObjects;                /*         list of all deleted objects                                */
  171.     CM_SHORT             useCount;                            /*        number of distinct users of this TOC            */
  172.     CMBoolean             refNumHandling;                /*        true ==> keep deleted object/value refNums*/
  173. };                                                                            /* Outside caller's see this as "void *"                */
  174. typedef struct TOC TOC, *TOCPtr;
  175.  
  176.  
  177. /* The index table accessing code assumes a big endian machine in determining whether        */
  178. /* shifts and ands can be used as a more efficient access than using divs and mods.  If    */
  179. /* there is indeed a problem in this area on other machines, set the following macro        */
  180. /* switch to 1.  Then the general divs and mods will always be used.                                        */
  181.  
  182. #ifndef PortabilityProblems
  183. #define PortabilityProblems 0
  184. #endif
  185.  
  186.  
  187. /*--------------------------------*
  188.  | cmCreateTOC - create a new TOC |
  189.  *--------------------------------*
  190.  
  191.  This routine MUST be called before any other TOC routine is called. It is used to set up
  192.  the data needed by the other TOC routines.  A "reference number" is returned to the
  193.  caller who must pass it to ALL the other TOC routines.  Any number of TOC can be created.
  194.  By passing the returned "reference number" to the other routines they will operate on the
  195.  corresponding TOC.
  196.  
  197.  Note, a TOC is "owned" by a container in the sense that each TOC is associated with a
  198.  particular container.  Container "objects" need to be able to get back at the container
  199.  control block that is associated with the TOC containing the object.  Thus the container
  200.  control block is passed here so that we may save it and store it into all created 
  201.  objects.
  202.  
  203.  Internally, i.e., in this file, the "reference number" is actually a pointer to a control
  204.  block containing the root pointer to the TOC index tables (set to NULL here) and the set
  205.  of variables needed to convert an object ID value into a set of indices which are used
  206.  to index into the index tables that lead to the actual TOC object corresponding to the
  207.  ID.  There is also a use count.  The use count is explained in cmUseTOC().
  208.  
  209.  This function returns the pointer if we can allocate the control block. NULL is returned
  210.  if the allocation fails.
  211.  
  212.  If we weren't being so "flexible" much if the code in here wouldn't be necessary!  We
  213.  could define all the table access variables as #define constants.  What we set up here
  214.  in the control block are the various divide and mod values we apply to the object ID to
  215.  extract the table indicies.
  216.  
  217.  We could potentially be more efficient in our index conversion by shifting and anding
  218.  instead of dividing and moding.  But that's only appropriate if the index table size is
  219.  a power of 2.  We could still use #defines if we took a specific power of 2.
  220.  
  221.  So there are a number of choices we can make: If an index table size is NOT a power of 2
  222.  we must use divides.  If it is a power of 2 we must determine the shift and mask. So,
  223.  given these choices, and to allow some degree in freedom of choosing an "appropriate"
  224.  table size, this routine does the extra work.
  225.  
  226.  cmCreateTOC() takes as its only parameter the index table size (tableSize) and initializes
  227.  the following data in the control block:
  228.  
  229.  nbrOfIndices         Number of index table levels (table depth).  Every access to an ID will
  230.                                  have to indirect through nbrOfIndices tables.  The value here is a
  231.                                 function of the chosen table size.
  232.                                 
  233.  tableSize            Size of an index table (the passed tableSize parameter). It may be any
  234.                                  value, but if a power of 2 is chosen we will determine a shift and mask
  235.                                 and use them instead of doing divides and mods.
  236.                                 
  237.  powerOfTwo        -    If tableSize is a power of 2 this will be true and the shift and mask set.
  238.  
  239.  indexShift        -    shift value to access an index (only if powerOfTwo is true).
  240.  
  241.  indexMask        -    mask value to access an index (only if powerOfTwo is true).
  242.  
  243.  An outside caller only sees the pointer to the control block as a void* reference. Thus
  244.  none of these variables are visible outside this file.
  245.  
  246.  Note, there could be some portability problems with this code!  If there are, e.g., the
  247.  target machine is not similar to the MC68XXX family, just set powerOfTwo in the TOC 
  248.  control block to false.  None of the shift/mod stuff will be done and it should work
  249.  anywhere!
  250. */
  251.  
  252. void *cmCreateTOC(CM_ULONG tableSize, const struct Container *container)
  253. {
  254.     CM_SHORT             bit, bitsPerLong;
  255.     CM_ULONG             maxValue;
  256.     TOCPtr                t;
  257.     
  258.     /* Allocate a TOC "control block" which contains the TOC root pointer and all                 */
  259.     /* associated access data.  The pointer to the control block will be returned as the    */
  260.     /* function result.                                                                                                                                        */
  261.  
  262.     t = (TOCPtr)CMmalloc(sizeof(TOC));                                /* a "control block" is born!                */
  263.     if (t == NULL) return (NULL);                                            /* well I thought it was born!            */
  264.     
  265.     t->container             = (ContainerPtr)container;            /* keep ptr to "owning" container        */
  266.     t->toc                       = NULL;                                                    /* 1st create will fill this in            */
  267.     t->deletedObjects = NULL;                                                    /* init list of deleted TOC objects    */
  268.     t->useCount                = 1;                                                        /* 1 user unless cmUseTOC() done        */
  269.     t->refNumHandling = true;                                                    /* always keep deleted refNums            */
  270.     
  271.     t->masterLists.objectsListHead        = t->masterLists.objectsListTail        = NULL;
  272.     t->masterLists.propertiesListHead    = t->masterLists.propertiesListTail    = NULL;
  273.     t->masterLists.typesListHead            = t->masterLists.typesListTail            = NULL;
  274.     
  275.     /* Set the size of each index table...                                                                                                */
  276.     
  277.     t->tableSize = tableSize;
  278.  
  279.     /* Determine the index table depth, i.e., the number of indicies we have to go                 */
  280.     /* through to get at a value.                                                                                                                    */
  281.     
  282.     t->nbrOfIndices = 1;                                                            /* there is always at least 1 index    */
  283.     maxValue = MaxValue;                                                            /* start with largest possible value*/
  284.     while (maxValue /= tableSize)                                         /* see how long it takes to reduce    */
  285.         ++t->nbrOfIndices;                                                            /*  the largest ID value to 0                */
  286.     
  287.     /* If PortabilityProblems is set to 1 then none of the code after here will be                 */
  288.     /* executed and divides and mods will always be done for index table accessing.                */
  289.     
  290.     #if PortabilityProblems
  291.     t->powerOfTwo = false;                                                        /* force divs and mods                            */
  292.     return ((void *)t);                                                                /* the other stuff remains garbage    */
  293.     #endif
  294.     
  295.     /* Determine the bits size of a "unsigned long".  This is done by counting the bits     */
  296.     /* in MaxValue which is, in turn, defined from ULONG_MAX in the standard C header         */
  297.     /* <limits.h>.  Note, it's not clear this helps with portability, but mods and divs        */
  298.     /* are used here instead of ands and shifts.                                                                                    */
  299.     
  300.     maxValue = MaxValue;                                                            /* this is 0xFFFFFFFF on MC68XXX's    */
  301.     bitsPerLong = 0;
  302.     while (maxValue) {
  303.         if ((maxValue % 2) != 0) ++bitsPerLong;                    /* ...count them bits!                            */
  304.         maxValue /= 2;
  305.     }
  306.     
  307.     /* Finally determine whether an index table size is a power of 2.  If it is we can        */
  308.     /* optimize a value's access by using shifts and masks rather than divides and mods.    */
  309.     /* If indeed a table size is a power of 2 (i.e., there is only 1 bit in tableSize),     */
  310.     /* then we set the shift and mask. These values are not used if powerOfTwo is set to     */
  311.     /* false.                                                                                                                                                            */
  312.     
  313.     for (t->powerOfTwo = false, bit = 0; bit < bitsPerLong; tableSize /= 2, ++bit) {
  314.         if ((tableSize % 2) != 0) {                                            /* if we got a bit...                                */
  315.             if (t->powerOfTwo) {                                                    /* ...but's it's not the only bit...*/
  316.                 t->powerOfTwo = false;                                            /* ...then we don't have power of 2 */
  317.                 break;                                                                            /* ...too bad (no sense going on)        */
  318.             }
  319.             t->powerOfTwo = true;                                                    /* ...if it is the 1st bit we see...*/
  320.             t->indexShift = bit;                                                    /* ...assume power of 2, set shift    */
  321.         }
  322.     }
  323.             
  324.     t->indexMask = t->tableSize - 1;                                    /* set mask...                                            */
  325.     
  326.     return ((void *)t);                                                                /* caller better take care of this!    */
  327. }
  328.  
  329.  
  330. /*------------------------------------------------*
  331.  | cmUseTOC - allow multiple uses of the same TOC |
  332.  *------------------------------------------------*
  333.  
  334.  This allows multiple users of the same TOC for the specified container.  This can come
  335.  about due to updating containers which want to use the TOC of their target.  They call
  336.  this routine to register the additional use of the specified TOC.  It is returned as the
  337.  function result but with the owning container of the TOC changed to the one specified.
  338.  Here we register the additional use by incrementing a use count of the TOC.  When
  339.  cmDestroyTOC() is called to remove the TOC it will decrement the use count.  Only when 
  340.  the count goes to 0 is the TOC actually destroyed.
  341.  
  342.  Since the owning container of the TOC is being changed, it is assumed the caller knows
  343.  what s/he is doing!  In particular we only do this for a container updating a target
  344.  and after the updates are applied, we want to use the target's TOC, but associate it with
  345.  the updating container.  This will allow new updates to be recorded in the updating
  346.  container, but in a TOC common to both the updater and the target.
  347. */
  348.  
  349. void *cmUseTOC(void *toc, struct Container *container)
  350. {
  351.     ++((TOCPtr)toc)->useCount;                                                     /* register the additiona use            */
  352.     ((TOCPtr)toc)->container = (ContainerPtr)container; /* set the new owning container        */
  353.     
  354.     return (toc);                                                                                /* caller can now use this TOC        */
  355. }
  356.  
  357.  
  358. /*-----------------------------------------------------------*
  359.  | getIndices - determine the indicies for a given object ID |
  360.  *-----------------------------------------------------------*
  361.  
  362.  This is an internal routine used when we must access an object through its ID.  We must
  363.  decompose the ID into the set of indicies we will apply to the corresponding index tables
  364.  as we indirect through them.  This routine does that decomposition.
  365.  
  366.  The ID is passed in and getIndices() builds an array of indicies in the indices array
  367.  also passed in.  It is assumed that the array is big enough.
  368.  
  369.  The function returns a pointer to the first index in the array.
  370.  
  371.  The array is built from last index to first.  Thus indicies[0] is the last index,
  372.  indicies[1] the next to last, and so on up to indicies[n], the first index to use and
  373.  the one we return a pointer to as the function result.  The n, of course is always equal
  374.  to nbrOfIndices.  If the ID produces less than nbrOfIndices indicies, we pad the remainder
  375.  with zeros.
  376.  
  377.  By returning the pointer to the first index, the indexing of the tables is ready to go
  378.  starting at the root, i.e., toc.  The array pointer is simply decremented to get the next
  379.  index.  This continues until the start of the array is reached.  The table you're in is
  380.  the last table and points to the data.
  381.  
  382.  Note, this is the routine that is sensitive to whether we have index tables with a power
  383.  of 2 size (as determined by tocInitialization() above).  Depending on powerOfTwo, we
  384.  either do shifts and masks or divides and mods.
  385. */
  386.  
  387. static CM_USHORT *CM_NEAR getIndices(const TOCPtr t, CMObjectID id, register CM_USHORT *indices)
  388. {
  389.     CM_SHORT i = 0;
  390.     
  391.     /* Extract the indicies used to index into each index table.  They are extracted from    */
  392.     /* low to high and placed into the indices array in ascending elements...                            */
  393.     
  394.     if (t->powerOfTwo)                                                                /* if we can shift and mask...            */
  395.         do {                                                                                        /* ...what are you waiting for?            */
  396.             *indices++ = (CM_USHORT)((CM_ULONG)id & t->indexMask);
  397.             id >>= t->indexShift;
  398.             ++i;
  399.         } while (id);
  400.     else                                                                                            /* if we can't do shifts...                    */
  401.         do {                                                                                        /* ...bummer!                                                */
  402.             *indices++ = (CM_USHORT)((CM_ULONG)id % t->tableSize);
  403.             id /= t->tableSize;
  404.             ++i;
  405.         } while (id);
  406.     
  407.     /* Fill in the remaining high order indices (if any) with zeros. Thus we always end   */
  408.     /* up with nbrOfIndices indices ready to index into their respective index tables.        */
  409.     
  410.     while (++i <= t->nbrOfIndices) *indices++ = 0;
  411.     
  412.     /* Return pointer to the high order index ready to be used by the caller...                        */
  413.     
  414.     return (indices - 1);                                                            
  415. }
  416.  
  417.  
  418. /*--------------------------------------*
  419.  | cmCreateObject - create a TOC object |
  420.  *--------------------------------------*
  421.  
  422.  This routine is called to create a new object in the specified TOC for the specified ID.
  423.  A "raw" object is created and the function returns a pointer to where it was placed in
  424.  the TOC. If there is already an object with that ID the TOC, a pointer to it is returned
  425.  and dup set to true. The function returns NULL if there is an allocation failure so that
  426.  we couldn't create the object.
  427.  
  428.  Note that the object created is "raw" in the sense that all info in it is NULLed out.
  429.  This is the lowest level routine whose sole goal in life is object creation.  The caller 
  430.  is responsible for maintaining the objects.  Life is simpler this way (not to mention
  431.  more maintainable)!
  432. */
  433.  
  434. TOCObjectPtr cmCreateObject(const void *toc, const CMObjectID objectID, 
  435.                                                         CM_USHORT objectFlags, CMBoolean *dup)
  436. {
  437.     register TOCPtr  t = (TOCPtr)toc;
  438.     register Indices *t1, *t2;
  439.     CM_ULONG                   i, tableSize = t->tableSize;
  440.     CM_USHORT            indices[32], *index, x;
  441.     CM_SHORT                 n = t->nbrOfIndices;
  442.     TOCObjectPtr          p;
  443.     ContainerPtr          container = t->container;    
  444.     
  445.     /* If this is the 1st time through here, there is no toc root pointer.  So we must        */
  446.     /* create the root index table to which toc will point from this time on.                            */
  447.     
  448.     if (t->toc == NULL) {                                                                        /* if 1st time...                            */
  449.         t->toc = (Indices)CMmalloc(tableSize * sizeof(void *));    /* ...create root                     */
  450.         if (t->toc == NULL) return (NULL);
  451.         for (i = 0; i < tableSize; i++) t->toc[i] = NULL;            /* ...init it to all NULLs        */
  452.     }
  453.     
  454.     /* Convert the ID to indicies and indirect through the indix tables. If along the way,*/
  455.     /* we discover "missing" tables for an index, we create those tables.  When all the        */
  456.     /* indicies are processed all the tables will be there to get at the object.                    */
  457.     
  458.     index = getIndices(t, objectID, indices);                                /* 1st get the indicies                */
  459.     
  460.     t1 = (Indices *)t->toc;                                                                    /* t1 will be current table        */
  461.     while (n-- > 1) {                                                                                /* use all the indicies...        */
  462.         t2 = (Indices *)t1[x = *index--];                                            /* t2 will be the "next" table*/
  463.                                                                                                                     /* x is index in current table*/
  464.         if (t2 == NULL) {                                                                            /* if we're missing a tbl...    */
  465.             t2 = (Indices *)CMmalloc(tableSize * sizeof(void *)); /* ...create it                            */
  466.             if (t2 == NULL) return (NULL);
  467.             for (i = 0; i < tableSize; i++) t2[i] = NULL;                /* ...set all entry to NULL        */
  468.             t1[x] = (Indices)t2;                                                                /* t1[x] points at new table    */
  469.         }
  470.         
  471.         t1 = t2;                                                                                            /* next table becomes current    */
  472.     }
  473.  
  474.     /* At this point we are now ready to use the index table corresponding to the last         */
  475.     /* index.  Entries in it point to the objects themselves and not other index tables.    */
  476.     /* For new objects the indexed entry better be NULL or we have a dup.                                    */
  477.     
  478.     if ((p = (TOCObjectPtr)t1[*index]) != NULL)                            /* if we have a dup...                */
  479.         *dup = true;                                                                                    /* ...tell caller                            */
  480.     else if ((p = (TOCObjectPtr)CMmalloc(sizeof(TOCObject))) != NULL){/* create new object*/
  481.         *dup                                 = false;                                                    /* ...tell caller it's new        */
  482.         t1[*index]                     = (Indices)p;                                            /* ...set ptr in table                */
  483.         cmInitList(&p->propertyList);                                                    /* ...set empty property list    */
  484.         cmInitList(&p->touchedList);                                                    /* ...set empty touched list    */
  485.         p->nextTouchedObject= NULL;                                                        /* ...not touched here                */
  486.         p->objectID               = objectID;                                                /* ...set the objects' ID            */
  487.         p->container              = container;                                            /* ...set "owning" container    */
  488.         p->nextObject             = p->prevObject             = NULL;            /* ...caller fills links in        */
  489.         p->nextTypeProperty = p->prevTypeProperty = NULL;            /* ...type/prop sub-chain too    */
  490.         p->objectFlags            = objectFlags;                                        /* ...info flags                            */
  491.         p->objectRefCon            = (CMRefCon)NULL;                                    /* ...refCon                                    */
  492.         p->useCount                    = 0;                                                            /* ...use count                                */
  493.     }
  494.     
  495.     return (p);                                                                                            /* ptr to new or dup object        */
  496. }
  497.  
  498.  
  499. /*-----------------------------------------------------------------------------------*
  500.  | cmUncreateObject - free memory for a single object and remove it from index table |
  501.  *-----------------------------------------------------------------------------------*
  502.  
  503.  This routine is the inverse (more-or-less) of cmCreateObject().  It removes an object
  504.  with the specified ID from the specified TOC.  The space for the object is freed.  The
  505.  lowest level index table entry pointing to the object is made NULL to indicate it is
  506.  undefined.  However, the higher level tables leading to the lowest table are not changed.
  507.  This doesn't hurt except to take up space if the object being deleted is the only one in
  508.  the tables leading to it.  But it's not worth all the extra work!
  509.  
  510.  As discussed in cmCreateObject() we simply create a "raw" object and return.  The caller
  511.  builds upon the object with lower level link structures. Unfortunately here, we have to 
  512.  allow the caller to handle his or her stuff BEFORE we delete the object.  Thus a
  513.  freeAction routine may be passed which is called prior to the freeing of the object. A
  514.  "refCon" is also provided which the caller can use as a communication facility to convey
  515.  additional info to the freeAction routine.
  516.  
  517.  Note, the freeAction may be passed as NULL, in which case only the object is deleted and
  518.  it is assumed averything linked to it must have already been deleted.
  519. */
  520.  
  521. void cmUncreateObject(const void *toc, const CMObjectID id, CMRefCon refCon,
  522.                                           void (*freeAction)(TOCObjectPtr object, CMRefCon refCon))
  523. {
  524.     CM_USHORT                              indices[32];
  525.     register CM_SHORT                n;
  526.     register CM_USHORT             *index;
  527.     register Indices                 *t;
  528.     TOCObjectPtr                        object;
  529.     ContainerPtr                         container = ((TOCPtr)toc)->container;    
  530.     
  531.     if (((TOCPtr)toc)->toc == NULL) return;                        /* safety                                                        */
  532.     
  533.     index = getIndices((TOCPtr)toc, id, indices);            /* get the ID's indicies                        */
  534.     
  535.     t = (Indices *)((TOCPtr)toc)->toc;                                /* indirect through index tables        */
  536.     n = ((TOCPtr)toc)->nbrOfIndices;                            
  537.     while (n-- > 1) {                                                                    /* loop through nbrOfIndices-1 tbls    */
  538.         t = (Indices *)t[*index--];                                            /* apply each index                                    */
  539.         if (t == NULL) return;                                                    /* not found if index table missing    */
  540.     } 
  541.     
  542.     object = (TOCObjectPtr)t[*index];                                    /* point at found object                        */
  543.     if (freeAction) (*freeAction)(object, refCon);        /* let caller putz with it                    */
  544.     
  545.     CMfree(object);                                                                        /* free the object                                    */
  546.     t[*index] = NULL;                                                                    /* remove it from lowest index tbl    */
  547. }
  548.  
  549.  
  550. /*-----------------------------------------------------------------------------------*
  551.  | cmPutObjectInTOC - free memory for a single object and remove it from index table |
  552.  *-----------------------------------------------------------------------------------*
  553.  
  554.  This routine let put put an object (currently belonging to another TOC) and put it into
  555.  this TOC. If an object of the same ID is already in this TOC, that old object in this
  556.  TOC will be replaced and also returned from the routine so that the caller can deal
  557.  with it.
  558.  
  559.  This routine is used to move objects from one TOC to another.
  560. */
  561.  
  562. void cmPutObjectInTOC(const void *toc, TOCObjectPtr object, TOCObjectPtr *replacedObject)
  563. {
  564.     register TOCPtr  t = (TOCPtr)toc;
  565.     register Indices *t1, *t2;
  566.     CM_ULONG                  i, tableSize = t->tableSize;
  567.     CM_USHORT            indices[32], *index, x;
  568.     CM_SHORT                 n = t->nbrOfIndices;
  569.     ContainerPtr          container = ((TOCPtr)toc)->container;    
  570.  
  571.     /* Convert the ID to indicies and indirect through the indix tables. If along the way,*/
  572.     /* we discover "missing" tables for an index, we create those tables.  When all the        */
  573.     /* indicies are processed all the tables will be there to get at the object.                    */
  574.     
  575.     index = getIndices(t, object->objectID, indices);                /* 1st get the indicies                */
  576.     
  577.     t1 = (Indices *)t->toc;                                                                    /* t1 will be current table        */
  578.     while (n-- > 1) {                                                                                /* use all the indicies...        */
  579.         t2 = (Indices *)t1[x = *index--];                                            /* t2 will be the "next" table*/
  580.                                                                                                                     /* x is index in current table*/
  581.         if (t2 == NULL) {                                                                            /* if we're missing a tbl...    */
  582.             t2 = (Indices *)CMmalloc(tableSize * sizeof(void *)); /* ...create it                            */
  583.             if (t2 == NULL) return;
  584.             for (i = 0; i < tableSize; i++) t2[i] = NULL;                /* ...set all entry to NULL        */
  585.             t1[x] = (Indices)t2;                                                                /* t1[x] points at new table    */
  586.         }
  587.         
  588.         t1 = t2;                                                                                            /* next table becomes current    */
  589.     }
  590.  
  591.     /* At this point we are now ready to use the index table corresponding to the last         */
  592.     /* index.  Entries in it point to the objects themselves and not other index tables.    */
  593.     /* For new objects the indexed entry better be NULL or we have a dup.                                    */
  594.     
  595.     if ((TOCObjectPtr)t1[*index])                                                        /* if we have a dup...                */
  596.         *replacedObject = (TOCObjectPtr)t1[*index];        /* ...tell caller about replaced one  */
  597.     else {
  598.         *replacedObject = NULL;                                                                /* ...tell caller it's new        */
  599.     }
  600.     t1[*index]                     = (Indices)object;                                /* ...set ptr in table                */
  601. }
  602.  
  603. /*-------------------------------------------------------------------------*
  604.  | cmDelete1Object - delete a single object and remove it from index table |
  605.  *-------------------------------------------------------------------------*
  606.  
  607.  This routine is the inverse (more-or-less) of cmCreateObject().  It removes an object
  608.  with the specified ID from the the specified TOC.  Unlike cmUncreateObject() however,
  609.  the space for the object is NOT freed.  Rather the object is RELINKED on to a singly
  610.  linked list of deleted objects pointed to by deletedObjects (a global).  This is done
  611.  to prevent dangling pointers that the API user may have as object "refNum"s (we give 
  612.  the API user these pointers as CMObject's).
  613.  
  614.  Since the object is removed from the TOC it will never be seen by us here using the TOC
  615.  index tables.  Just as in cmUncreateObject() the lowest level index table entry pointing
  616.  to the object is made NULL to indicate it is undefined in the TOC.
  617.  
  618.  Also as in cmUncreateObject() we allow the caller to do something with the object before
  619.  it is deleted from the TOC and before we link it on to the deletedObjects list.  The
  620.  calling conventions are the same and NULL may be passed to indicate no action is to be
  621.  performed.
  622.  
  623.  Caution: the deletedObjects list utilizes the nextObject field of an object to link its
  624.  objects.  That field is also a link field for active objects.  Thus the object must be
  625.  unlinked from the active object chain BEFORE calling this routine. 
  626. */
  627.  
  628. void cmDelete1Object(const void *toc, const CMObjectID id, CMRefCon refCon,
  629.                                        void (*deleteAction)(TOCObjectPtr object, CMRefCon refCon))
  630. {
  631.     CM_USHORT                              indices[32];
  632.     register CM_SHORT                n;
  633.     register CM_USHORT             *index;
  634.     register Indices                 *t;
  635.     TOCObjectPtr                        object;
  636.     ContainerPtr                        container;
  637.     
  638.     if (((TOCPtr)toc)->toc == NULL) return;                        /* safety                                                        */
  639.     
  640.     index = getIndices((TOCPtr)toc, id, indices);            /* get the ID's indicies                        */
  641.     
  642.     t = (Indices *)((TOCPtr)toc)->toc;                                /* indirect through index tables        */
  643.     n = ((TOCPtr)toc)->nbrOfIndices;
  644.     while (n-- > 1) {                                                                    /* loop through nbrOfIndices-1 tbls    */
  645.         t = (Indices *)t[*index--];                                            /* apply each index                                    */
  646.         if (t == NULL) return;                                                    /* not found if index table missing    */
  647.     } 
  648.     
  649.     object = (TOCObjectPtr)t[*index];                                    /* point at found object                        */
  650.     if (deleteAction) (*deleteAction)(object, refCon);/* let caller putz with it                    */
  651.     
  652.     if (((TOCPtr)toc)->refNumHandling) {                            /* if keep deleted object refNums...*/
  653.         object->nextObject = ((TOCPtr)toc)->deletedObjects;    /* link to deletedObjects                */
  654.         ((TOCPtr)toc)->deletedObjects = object;                    /* list rather than freeing it            */
  655.         object->objectFlags |= DeletedObject;                        /* flag object as now deleted                */
  656.     } else {                                                                                    /* if freeing deleted refNums...        */
  657.         container = ((TOCPtr)toc)->container;                        /* ...needed for CMfree()                        */
  658.         CMfree(object);                                                                    /* ...free the object space                    */
  659.     }
  660.     
  661.     t[*index] = NULL;                                                                    /* remove it from lowest index tbl    */
  662. }
  663.  
  664. /*-----------------------------------------------*
  665.  | cmFindObject - find the specified object (ID) |
  666.  *-----------------------------------------------*
  667.  
  668.  Given an object ID in a TOC, this function returns a pointer to the corrsponding object
  669.  or NULL if the object does not exist.
  670. */
  671.  
  672. TOCObjectPtr cmFindObject(const void *toc, const CMObjectID id)
  673. {
  674.     CM_USHORT                              indices[32];
  675.     register CM_SHORT                n;
  676.     register CM_USHORT             *index;
  677.     register Indices                 *t;
  678.  
  679.     if (((TOCPtr)toc)->toc == NULL) return (NULL);        /* safety                                                        */
  680.     
  681.     index = getIndices((TOCPtr)toc, id, indices);            /* get the ID's indicies                        */
  682.     
  683.     t = (Indices *)((TOCPtr)toc)->toc;                                /* indirect through index tables        */
  684.     n = ((TOCPtr)toc)->nbrOfIndices;                                
  685.     while (n-- > 1) {                                                                    /* loop through nbrOfIndices-1 tbls    */
  686.         t = (Indices *)t[*index--];                                            /* apply each index                                    */
  687.         if (t == NULL) return (NULL);                                        /* not found if index table missing    */
  688.     } 
  689.     
  690.     return ((TOCObjectPtr)(t[*index]));                                /* return object ptr or NULL                */
  691. }
  692.  
  693.  
  694. /*---------------------------------------------------------------------------------*
  695.  | scanIndexTable - scan all entries of an index table acting on each object entry |
  696.  *---------------------------------------------------------------------------------*
  697.  
  698.  This routine is the "guts" of cmForEachObject() used to access all objects in the TOC and
  699.  perform some action on them.  A pointer to a index table is passed in t.  The index table
  700.  level is passed in indexLevel (i.e., a value from 1 to nbrOfIndices, with 1 corresponding
  701.  to the first index).  If t corresponds to the deepest table, i.e., the one that contains
  702.  object pointers, we call the action to do something with the object.
  703.  
  704.  The table pointer and index level are of no concern to outside callers.  That is why
  705.  cmForEachObject() is a cover routine.  It set the initial t and indexLevel (toc and 1).
  706.  It is also needed to start the recursion.
  707.  
  708.  This routine is recursive as it decends down through the tables.  It will access the
  709.  objects in ascending order and stop on the first object that has an ID greater than the
  710.  endingID.  The initial starting point in each index table is determined from the index
  711.  parameter.  It points into an array of starting indicies for each index table level.
  712.  Once used, the array element is set to 0 to use all entries in all following tables at
  713.  that level.
  714.  
  715.  See cmForEachObject() for details on the refCon and action parameters.
  716. */
  717.  
  718. static void CM_NEAR scanIndexTable(register const TOCPtr toc, CM_USHORT *index,
  719.                                                                      CMObjectID endingID, const Indices *t,
  720.                                                                      const CM_SHORT indexLevel, CMRefCon refCon,
  721.                                                                      void (*action)(TOCObjectPtr object, CMRefCon refCon))
  722. {
  723.     CM_ULONG            i;
  724.     TOCObjectPtr     p;
  725.     ContainerPtr     container;
  726.  
  727.     if (indexLevel < toc->nbrOfIndices)                                                    /* if we have index tbl...*/
  728.         for (i = *index, *index-- = 0; i < toc->tableSize; i++) {    /* ...look at each entry    */
  729.             if (t[i])                                                                                             /* ...if non-NULL entry...*/
  730.                 scanIndexTable(toc, index, endingID, (Indices *)t[i], (CM_SHORT)(indexLevel+1), refCon, action);
  731.         }
  732.     else                                                                                                                 /* if last index table...    */
  733.         for (i = *index, *index = 0; i < toc->tableSize; i++)            /* ...do each entry                */
  734.             if ((p = (TOCObjectPtr)(t[i])) != NULL)                                 /* ...if non-NULL entry...*/
  735.                 if (p->objectID <= endingID)                                                    /* ...and not at max ID...*/
  736.                     (*action)(p, refCon);                                                                /* ...process the action    */
  737.                 else {                                                                                                /* ...if at ending ID...    */
  738.                     container = toc->container;
  739.                     SessionSuccess = true;                                                            /* ...return success            */
  740.                     AbortForEachObject(1);                                                            /* ...abort walk                    */
  741.                 }
  742. }
  743.  
  744.  
  745. /*-------------------------------------------------*
  746.  | cmForEachObject - do some action on each object |
  747.  *-------------------------------------------------*
  748.  
  749.  Do (call) the specified action for each object in the specified TOC with object ID's
  750.  greater than or equal to the startingID and less than or equal to endingID.  The pointer
  751.  to each TOC object is passed to the action routine along with a "refCon" which the caller
  752.  can use as a communication facility to convey additional info to the action routine.  0
  753.  is returned to indicate successful completion.  Use the AbortForEachObject(x) (a macro)
  754.  in the action routine to abort the interator.  The "x" should be a positive integer which
  755.  is returned from cmForEachObject() and it should not be 0.
  756. */
  757.  
  758. int cmForEachObject(const void *toc, CMObjectID startingID, CMObjectID endingID, 
  759.                                          CMRefCon refCon, void (*action)(TOCObjectPtr object, CMRefCon refCon))
  760. {
  761.     CM_SHORT              n;
  762.     CM_USHORT          *index, indices[32];
  763.     ContainerPtr      container = ((TOCPtr)toc)->container;
  764.  
  765.     if (((TOCPtr)toc)->toc) {                                                    /* scanIndexTable() does everything    */
  766.         if (startingID == ALLOBJECTS) {                                    /* if frocessing ALL objects...            */
  767.             n = ((TOCPtr)toc)->nbrOfIndices;                            /* ...init indices array to all 0's    */
  768.             index = indices - 1;                                                    /*         the 0 index will cause all         */
  769.             while (n-- > 0) *++index = 0;                                    /*        index table entrys to be used    */
  770.         } else                                                                                    /* if explicit starting ID...                */
  771.             index = getIndices((TOCPtr)toc, startingID, indices); /* ...init indices from ID    */
  772.  
  773.         SessionSuccess = false;                                                    /* assume walk fails with longjmp        */
  774.  
  775.         if (setjmp(SESSION->cmForEachObjectEnv))                /* if longjmp taken...                            */
  776.             return (SessionSuccess ? 0 : 1);                            /* ...failure unless we hit endingID*/ 
  777.         
  778.         scanIndexTable((TOCPtr)toc, index, endingID, (Indices *)((TOCPtr)toc)->toc, 1, refCon, action);
  779.     }
  780.     
  781.     return (0);                                                                                /* success                                                    */
  782. }
  783.  
  784.  
  785. /*------------------------------------------------------------*
  786.  | freeAllObjects - actual implementation for freeEntireTOC() |
  787.  *------------------------------------------------------------*
  788.  
  789.  This routine is the "guts" of freeEntireTOC() used to access all objects in the TOC and
  790.  free them.  A pointer to a index table is passed in t.  The index table level is passed
  791.  in indexLevel (i.e., a value from 1 to nbrOfIndices, with 1 corresponding to the first
  792.  index).  If t corresponds to the deepest table, i.e., the one that contains object 
  793.  pointers, we free the object.
  794.  
  795.  The table pointer and index level are of no concern to outside callers.  That is why
  796.  freeEntireTOC() is a cover routine.  It set the initial t and indexLevel (toc and 1).
  797.  
  798.  This routine is recursive as it descends down through the tables.  It will access the
  799.  objects free them.  As we ascend back up the index tables themselves are freed.  By the
  800.  time we're done, the entire TOC will cease to exist!
  801.  
  802.  See destroyTOC() for details on the refCon and deleteAction parameters.
  803. */
  804.  
  805. static void CM_NEAR freeAllObjects(register const TOCPtr toc, const Indices *t,
  806.                                                                      const CM_SHORT indexLevel, CMRefCon refCon, CMBoolean objectToo,
  807.                                                                      void (*deleteAction)(TOCObjectPtr object, CMRefCon refCon))
  808. {
  809.     CM_ULONG            i;
  810.     TOCObjectPtr     object;
  811.     ContainerPtr  container = toc->container;
  812.  
  813.     if (indexLevel < toc->nbrOfIndices)                                /* if we have index table...                */
  814.         for (i = 0; i < toc->tableSize; i++) {                    /* ...look at each entry...                    */
  815.             if (t[i])                                                                         /* ...if non-NULL entry...                    */
  816.                 freeAllObjects(toc, (Indices *)t[i], (CM_SHORT)(indexLevel+1), refCon, objectToo, deleteAction);
  817.         }
  818.     else                                                                                            /* if last index table...                        */
  819.         for (i = 0; i < toc->tableSize; i++)                        /* ...we still look at each entry...*/
  820.             if ((object = (TOCObjectPtr)(t[i])) != NULL) {/* ...if non-NULL entry...                    */
  821.                 if (objectToo) {                                                            /* only if we want to free object        */
  822.                     if (deleteAction)                                                        /* ...and if caller wants a crack...*/
  823.                         (*deleteAction)(object, refCon);                    /* ...let caller do his thing                */
  824.                     CMfree(object);                                                            /* ...free the object itself                */
  825.                 }
  826.             }
  827.             
  828.     CMfree(t);                                                                                /* free the index table too                    */
  829. }
  830.  
  831.  
  832. /*-----------------------------------------------------------------------*
  833.  | cmDestroyTOC - free all TOC objects and their associated index tables |
  834.  *-----------------------------------------------------------------------*
  835.  
  836.  This routine is called to remove wipe a TOC out of existence.  All existing objects in
  837.  the TOC, supporting index tables, and deleted objects on the deletedObjects list are
  838.  freed.  The "reference number" must not be used beyond this point.  The TOC pointer is
  839.  NULLed out prior to return to prevent further use.
  840.  
  841.  Note, that anything "below" an object entry is NOT handled here, i.e., all stuff that 
  842.  the object might be pointing to.  The caller MUST provide a way of deleting that stuff.
  843.  To this end, the caller should provide a "deleteAction" routine.  It is called just
  844.  prior to deleting each object.  The pointer to each object is passed to the deleteAction
  845.  routine along with a "refCon" which the caller can use as a communication facility to
  846.  convey additional info to the deleteAction routine.
  847.  
  848.  The objectToo flag permits you to destroy the toc without deleting the objects.
  849.  
  850.  The deleteAction value may be passed as NULL.  If that is the case, then (obviously) no
  851.  call is done and the object is simply deleted.  It's not clear anyone will do such a
  852.  thing, but it's provided anyway!
  853.  
  854.  Since multiple users of the same TOC are permitted (because of updating), a use count is
  855.  maintained of the number of users of the TOC.  Additional users call cmUseTOC() to 
  856.  increment the useCount.  It is decremented here.  Only when the count goes to 0 do is
  857.  the TOC actually destroyed.
  858. */
  859.  
  860. void cmDestroyTOC(void **toc, CMRefCon refCon, CMBoolean objectToo,
  861.                                     void (*deleteAction)(TOCObjectPtr object, CMRefCon refCon))
  862. {
  863.     TOCObjectPtr theObject, nextObject;
  864.     ContainerPtr container = ((TOCPtr)*toc)->container;
  865.     
  866.     if (--((TOCPtr)*toc)->useCount > 0) return;                /* don't destroy if multiple users    */
  867.     
  868.     if (((TOCPtr)*toc)->toc)                                                     /* bye, bye TOC...                                    */
  869.         freeAllObjects((TOCPtr)*toc, (Indices *)((TOCPtr)*toc)->toc, 1, refCon, objectToo, deleteAction);
  870.     
  871.     /* Free all objects that we removed from the TOC and placed on deletedObjects list.     */
  872.     /* We assume here that such objects placed on this list have no substructures tied to    */
  873.     /* them.  Thus we DON'T call the action routine for each object freed here.                        */
  874.     
  875.     theObject = ((TOCPtr)*toc)->deletedObjects;                /* these kept around to stop user        */
  876.                                                                                                         /* from using dangling pointers            */
  877.     while (theObject) {                                                                /* go through entire list...                */
  878.         nextObject = theObject->nextObject;
  879.         cmDeleteTouchedList(theObject, container);            /* free touched list (if any)                */
  880.         CMfree(theObject);                                                            /* that's end of this object                */
  881.         theObject = nextObject;
  882.     }
  883.  
  884.     CMfree(*toc);                                                                            /* bye, bye TOC control block                */
  885.     *(TOCPtr **)toc = NULL;                                                        /* kill caller's toc pointer                */
  886. }
  887.  
  888.  
  889. /*-----------------------------------------------------------------------------*
  890.  | cmLinkObject - link an object into the master object chains for a container |
  891.  *-----------------------------------------------------------------------------*
  892.  
  893.  This routine is (doubly) links an object (theObject) into the master object chains whose
  894.  head and tail pointers are contained in the specified TOC.  The objects is inserted in
  895.  the object chain following afterThisObject. If afterThisObject is NULL the object is
  896.  appended to the end of its list(s).  The function returns the input object pointer as its
  897.  result.
  898.  
  899.  There are three master chains:
  900.  
  901.          (1).  A chain of property objects.
  902.         (2).  A chain of type objects.
  903.          (3).  A chain of ALL objects no matter what their type.  It may be neither a type or
  904.               property.
  905.                     
  906.  Chain (3) is always built. The decision on whether to do (1) or (2) is a function of the
  907.  objectFlags field contained in the object itself.  So the caller must have set these
  908.  prior to calling this routine.
  909.  
  910.  Note, the algorithm here is identical to the generic list processing routines.  But we
  911.  don't have total freedom of where to put the link fields since we have more than one set
  912.  of links!  So we reinvent the wheel and do the code explicitly here (three times in
  913.  fact).
  914. */
  915.  
  916. TOCObjectPtr cmLinkObject(const void *toc, TOCObjectPtr theObject, 
  917.                                                     TOCObjectPtr afterThisObject)
  918. {
  919.     /* We must not link the object more that once to its master lists. We could be calling*/
  920.     /* this routine as a result of defining additional properties and values for an             */
  921.     /* existing object.  So we prevent the multiple attempts by flagging the object the        */
  922.     /* first time we actually do link it.  Then we just exits for additional attempts.        */
  923.     
  924.     if ((theObject->objectFlags & LinkedObject) != 0) return (theObject);
  925.         
  926.     /* Insert in the "all objects" chain...                                                                                                */
  927.     
  928.     if (afterThisObject == NULL) {                                                        /* append to end of list...    */
  929.         theObject->nextObject = NULL;
  930.         theObject->prevObject = ((TOCPtr)toc)->masterLists.objectsListTail;
  931.         if (((TOCPtr)toc)->masterLists.objectsListTail == NULL)
  932.             ((TOCPtr)toc)->masterLists.objectsListHead = theObject;
  933.         else
  934.             ((TOCPtr)toc)->masterLists.objectsListTail->nextObject = theObject;
  935.         ((TOCPtr)toc)->masterLists.objectsListTail = theObject;
  936.     } else {                                                                                                    /* insert after...                    */
  937.         theObject->prevObject = afterThisObject;
  938.         theObject->nextObject = afterThisObject->nextObject;
  939.         if (afterThisObject->nextObject)
  940.             afterThisObject->nextObject->prevObject = theObject;
  941.         else
  942.             ((TOCPtr)toc)->masterLists.objectsListTail = theObject;
  943.         afterThisObject->nextObject = theObject;
  944.     }
  945.     
  946.     /* Insert in the "properties" or "type" list if it's one of those types...                        */
  947.     
  948.     if (theObject->objectFlags & PropertyObject) {                        /* property...                            */
  949.         if (afterThisObject == NULL) {                                                    /*         append...                            */
  950.             theObject->nextTypeProperty = NULL;
  951.             theObject->prevTypeProperty = ((TOCPtr)toc)->masterLists.propertiesListTail;
  952.             if (((TOCPtr)toc)->masterLists.propertiesListTail == NULL)
  953.                 ((TOCPtr)toc)->masterLists.propertiesListHead = theObject;
  954.             else
  955.                 ((TOCPtr)toc)->masterLists.propertiesListTail->nextTypeProperty = theObject;
  956.             ((TOCPtr)toc)->masterLists.propertiesListTail = theObject;
  957.         } else {                                                                                                /*        insert after...                */
  958.             theObject->prevTypeProperty = afterThisObject;
  959.             theObject->nextTypeProperty = afterThisObject->nextTypeProperty;
  960.             if (afterThisObject->nextTypeProperty)
  961.                 afterThisObject->nextTypeProperty->prevTypeProperty = theObject;
  962.             else
  963.                 ((TOCPtr)toc)->masterLists.propertiesListTail = theObject;
  964.             afterThisObject->nextTypeProperty = theObject;
  965.         }
  966.     } else if (theObject->objectFlags & TypeObject) {                    /* type...                                    */
  967.         if (afterThisObject == NULL) {                                                    /*        append...                            */
  968.             theObject->nextTypeProperty = NULL;
  969.             theObject->prevTypeProperty = ((TOCPtr)toc)->masterLists.typesListTail;
  970.             if (((TOCPtr)toc)->masterLists.typesListTail == NULL)
  971.                 ((TOCPtr)toc)->masterLists.typesListHead = theObject;
  972.             else
  973.                 ((TOCPtr)toc)->masterLists.typesListTail->nextTypeProperty = theObject;
  974.             ((TOCPtr)toc)->masterLists.typesListTail = theObject;
  975.         } else {                                                                                                /*        insert after...                */
  976.             theObject->prevTypeProperty = afterThisObject;
  977.             theObject->nextTypeProperty = afterThisObject->nextTypeProperty;
  978.             if (afterThisObject->nextTypeProperty)
  979.                 afterThisObject->nextTypeProperty->prevTypeProperty = theObject;
  980.             else
  981.                 ((TOCPtr)toc)->masterLists.typesListTail = theObject;
  982.             afterThisObject->nextTypeProperty = theObject;
  983.         }
  984.     }
  985.     
  986.     /* Flag the object so we never try to do this crap again!  See start of code above.        */
  987.     
  988.     theObject->objectFlags |= LinkedObject;
  989.     
  990.     return (theObject);
  991. }
  992.  
  993.  
  994. /*---------------------------------------------------------------------------------*
  995.  | cmUnlinkObject - unlink an object from the master object chains for a container |
  996.  *---------------------------------------------------------------------------------*
  997.  
  998.  This routine is the inverse to cmLinkObject().  It unlinks the specified object from the
  999.  master object chains whose head and tail pointers are contained in the specified TOC.
  1000.  The function returns the input object pointer as its result.
  1001.  
  1002.  The object is always deleted from the chain of all objects.  It is also deleted from the
  1003.  property or type chain if the objectFlags field contained in the object itself indicates
  1004.  it is a property or type (see cmLinkObject() above for further details about the chains).
  1005. */
  1006.  
  1007. TOCObjectPtr cmUnlinkObject(const void *toc, TOCObjectPtr theObject)
  1008. {
  1009.     /* We must not unlink the object more that once from its master lists...                            */
  1010.     
  1011.     if ((theObject->objectFlags & LinkedObject) == 0) return (theObject);
  1012.         
  1013.     /* Delete from the "all objects" chain...                                                                                            */
  1014.  
  1015.     if (theObject->prevObject)
  1016.         theObject->prevObject->nextObject = theObject->nextObject;
  1017.     else                                                                                                                     /* delete head                     */
  1018.         ((TOCPtr)toc)->masterLists.objectsListHead = theObject->nextObject;
  1019.     if (theObject->nextObject)
  1020.         theObject->nextObject->prevObject = theObject->prevObject;
  1021.     else                                                                                                                     /* delete tail                     */
  1022.         ((TOCPtr)toc)->masterLists.objectsListTail = theObject->prevObject;
  1023.     
  1024.     /* Delete from the "properties" or "type" list if it's one of those types...                    */
  1025.     
  1026.     if (theObject->objectFlags & PropertyObject) {
  1027.         if (theObject->prevObject)
  1028.             theObject->prevObject->nextObject = theObject->nextObject;
  1029.         else                                                                                                                 /* delete head                     */
  1030.             ((TOCPtr)toc)->masterLists.propertiesListHead = theObject->nextObject;
  1031.         if (theObject->nextObject)
  1032.             theObject->nextObject->prevObject = theObject->prevObject;
  1033.         else                                                                                                                 /* delete tail                     */
  1034.             ((TOCPtr)toc)->masterLists.propertiesListTail = theObject->prevObject;
  1035.     } else if (theObject->objectFlags & TypeObject) {
  1036.         if (theObject->prevObject)
  1037.             theObject->prevObject->nextObject = theObject->nextObject;
  1038.         else                                                                                                                 /* delete head                     */
  1039.             ((TOCPtr)toc)->masterLists.typesListHead = theObject->nextObject;
  1040.         if (theObject->nextObject)
  1041.             theObject->nextObject->prevObject = theObject->prevObject;
  1042.         else                                                                                                                 /* delete tail                     */
  1043.             ((TOCPtr)toc)->masterLists.typesListTail = theObject->prevObject;
  1044.     }
  1045.     
  1046.     /* Remove the "has been linked" flag for so we can't unlink it twice...                                */
  1047.     
  1048.     theObject->objectFlags &= ~LinkedObject;
  1049.  
  1050.     return (theObject);
  1051. }
  1052.  
  1053.  
  1054. /*----------------------------------------------------------------*
  1055.  | cmGetMasterListHead - get head of specified master object list |
  1056.  *----------------------------------------------------------------*
  1057.  
  1058.  This routine returns the head of one of the master chain lists in a TOC depending on 
  1059.  the objectKindFlag.  This flag should be one of the standard object flags; ObjectObject,
  1060.  PropertyObject, or TypeObject.  The respective chain head will be returned as the function
  1061.  result.
  1062.  
  1063.  These chain heads are used by the CMGetNextObject(), CMGetNextProperty(), and
  1064.  CMGetNextType() routines to start them off.
  1065. */
  1066.  
  1067. TOCObjectPtr cmGetMasterListHead(const void *toc, CM_USHORT objectKindFlag)
  1068. {
  1069.     if (objectKindFlag == ObjectObject) 
  1070.         return (((TOCPtr)toc)->masterLists.objectsListHead);
  1071.     
  1072.     if (objectKindFlag == PropertyObject)
  1073.         return (((TOCPtr)toc)->masterLists.propertiesListHead);
  1074.         
  1075.     return (((TOCPtr)toc)->masterLists.typesListHead);
  1076. }
  1077.  
  1078. /*----------------------------------------------------------------*
  1079.  | cmGetMasterListTail - get tail of specified master object list |
  1080.  *----------------------------------------------------------------*
  1081.  
  1082.  This routine returns the tail of one of the master chain lists in a TOC depending on 
  1083.  the objectKindFlag.  This flag should be one of the standard object flags; ObjectObject,
  1084.  PropertyObject, or TypeObject.  The respective chain head will be returned as the function
  1085.  result.
  1086.  
  1087.  These chain tails are used by the CMGetPrevObject(), CMGetPrevProperty(), and
  1088.  CMGetPrevType() routines to start them off.
  1089. */
  1090.  
  1091. TOCObjectPtr cmGetMasterListTail(const void *toc, CM_USHORT objectKindFlag)
  1092. {
  1093.     if (objectKindFlag == ObjectObject) 
  1094.         return (((TOCPtr)toc)->masterLists.objectsListTail);
  1095.     
  1096.     if (objectKindFlag == PropertyObject)
  1097.         return (((TOCPtr)toc)->masterLists.propertiesListTail);
  1098.         
  1099.     return (((TOCPtr)toc)->masterLists.typesListTail);
  1100. }
  1101.  
  1102. /*--------------------------------------------------------------------------*
  1103.  | cmChangeRefNumHandling - change status of deleted user "refNum" handling |
  1104.  *--------------------------------------------------------------------------*
  1105.  
  1106.  Normally all deleted values and objects are "remembered".  A value's header is placed on
  1107.  the deleted values list attacthed to the container control block.  Deleted TOCObject's
  1108.  are placed on the deleted object list attached to the toc control block.  Both value
  1109.  headers and TOCObject's are the refNums we give to the user.  By remembering these we
  1110.  can check for attempted reuse and report an error.
  1111.  
  1112.  However, there are times we don't want to track these things because we know internally
  1113.  what we're doing (well, that's the theory).  So an override switch is provided.  Since
  1114.  both kinds of refNums are TOC entities, the override is on a TOC basis and hence the
  1115.  switch attached to the TOC control block.
  1116.  
  1117.  This routine is therefore provided to change the setting the override switch.  It is a
  1118.  boolean with true meaning that deleted refNums are to be saved on their respective lists.
  1119.  Setting the switch to false will cause the refNum to simply be freed.
  1120.  
  1121.  By calling cmKeepDeletedRefNums(), the current setting of the switch can be interrogated
  1122.  to decide what to do.
  1123.  
  1124.  Oh, in case you're curious, one case where we suppress the refNum tracking is when we are
  1125.  applying updates at open time where updating instructions are being interpreted to 
  1126.  update a target container.
  1127. */
  1128.  
  1129. void cmChangeRefNumHandling(const void *toc, CMBoolean setting)
  1130. {
  1131.     ((TOCPtr)toc)->refNumHandling = setting;        /* ...set new state                                                */
  1132. }
  1133.  
  1134.  
  1135. /*---------------------------------------------------------------------------------*
  1136.  | cmKeepDeletedRefNums - return current status of the handling of deleted refNums |
  1137.  *---------------------------------------------------------------------------------*
  1138.  
  1139.  As described above, this is used by all outside callers to see if a refNum is to be saved
  1140.  on its deleted list.  The only thing outside callers can possibly be interested in is
  1141.  for deleted values and whether the value headers are to be saved on the deleted values
  1142.  list.
  1143. */
  1144.  
  1145. CMBoolean cmKeepDeletedRefNums(const void *toc)
  1146. {
  1147.     return (((TOCPtr)toc)->refNumHandling);            /* does this really need a comment?                */
  1148. }
  1149.                                                         
  1150.                                                           CM_END_CFUNCTIONS
  1151.